<?php

namespace App\Command\ResumeParsing;

use App\Web\Model\RabbitMQ\ResumeUpdate;
use App\Service\FileHandler;
use EchoPHP\File\File as FileHelper;
use EchoPHP\Facades\File;
use App\Web\Model\CaseModel;
use EchoPHP\Console\Command;
use EchoPHP\Facades\Db as Db;
use EchoPHP\Facades\Log as Log;
use App\Web\Model\Resume as ResumeModel;

use App\Command\ResumeParsing\Service as ParsingService;
use PhpOffice\PhpSpreadsheet;

class FileToResume extends Command {
    //https://blog.csdn.net/nomandia/article/details/80190619
    // php /www/RPO-pro/Command ResumeParsing:FileToResume
    protected $signature = "ResumeParsing:FileToResume";
    protected $description = '上传简历文件解析服务';
    public $fileServer = '';
    protected $uploadDirectory = '';
    protected $attachDirectory = '';
    private $readFileHelper;
    public $parsingHelper;
    private $translateHelper;
    private $excelPreprocessors;
    private $avatarDir;
    private $tmpDir;
    private $enableMachineLearning;
    private $useMachineLearning;
    private $useMachineLearningOnly;
    private $checkRepeat;

    /**
     * Create a new command instance.
     *
     * @return void
     * @throws \Exception
     */
    public function __construct() {
        $this->fileServer = FILESERVER;
        $machineLearningMode = config('app.resume_machine_learning.mode', 0);
        $this->enableMachineLearning = $machineLearningMode > 0;
        $this->useMachineLearning = boolval($machineLearningMode & 1);
        $this->useMachineLearningOnly = $machineLearningMode == 1;
        $this->uploadDirectory = config('app.file_upload.directory');
        $this->attachDirectory = config('app.file_attach.directory');
        $this->avatarDir = config('app.file_resumehead.directory');
        $this->checkRepeat = config('app.resume_machine_learning.check_repeat');

        $this->tmpDir = $this->attachDirectory . '/tmp/' . date('Y_m') . '/';
        if (!is_dir($this->tmpDir)) {
            if (file_exists($this->tmpDir)) @unlink($this->tmpDir);
            File::makeDirectory($this->tmpDir, 0766, true);
        }
        parent::__construct();
        $this->readFileHelper = new ParsingService\ReadFileHelper();
        $this->parsingHelper = new ParsingService\ParsingHelper();
        $this->translateHelper = new ParsingService\TranslateHelper();
        $this->excelPreprocessors = [];
        foreach ($this->parsingHelper->specialParsers as $i => $parser) {
            if ($parser instanceof ParsingService\ParsingSpecial\PreprocessorExcel)
                $this->excelPreprocessors[$i] = $parser;
        }
    }

    /*
     * 解析服务
     */
    public function handle() {
        ini_set('memory_limit', '512M');
        mb_internal_encoding("UTF-8");
        //初始化进度信息
        $positionConfig = __DIR__ . "/position.txt";
        $positionArr = $this->getConfig($positionConfig);

        $beginTime = time();
        $runFlag = true;
        //while ($runFlag && time() - $beginTime < 1 * 60) {
        while ($runFlag && time() - $beginTime < (10 * 60 - 10)) {
            try {
                $taskInfo = array();

                $sql = "select id,attachname,filepath,source,creator,createdate,extraparam from ResumeOCRTask where id>=? and status=? and createdate < SUBTIME(NOW(),'0:0:2') order by id asc";
                $var = [$positionArr['taskid'] - 1000, '01'];
                $taskList = Db::select($sql, $var, 0, 10);
                if (empty($taskList) || count($taskList) < 1) {
                    $this->output->writeln("没有解析任务：" . date("Y-m-d H:i:s") . PHP_EOL);
                    sleep(2);
                    continue;
                }
                //测试机器学习服务器状态
                $machineLearningStatus = $this->checkMachineLearning();
                if (!$machineLearningStatus) {
                    sleep(20);
                    break;
                }

                foreach ($taskList as $taskInfo) {
                    $sql = 'UPDATE ResumeOCRTask SET status=?,startdate=now() WHERE id=? AND status=?';
                    $var = ['05', $taskInfo['id'], '01'];
                    $fileInfo = Db::update($sql, $var);
                    if ($fileInfo === 1) {
                        break;
                    }
                }

                $positionArr['taskid'] = $taskInfo['id'];
                file_put_contents($positionConfig, json_encode($positionArr));
                if (empty($taskInfo)) {
                    continue;
                }

                $taskInfo['caseid'] = 0;
                if (!empty($taskInfo['extraparam'])) {
                    $extraParamArr = @unserialize($taskInfo['extraparam']);
                    if (!empty($extraParamArr) && is_array($extraParamArr) && isset($extraParamArr['caseid']) && isset($extraParamArr['caseid']) > 0) {
                        $taskInfo['caseid'] = $extraParamArr['caseid'];
                    }
                }

                //  unserialize(CommonFun::htmlDecodeStr($value['inputbuf']));    $inputBuf = serialize($inputParam);

                $taskInfo['delfile'] = false;
                $attachPostfix = strtolower(substr(strrchr($taskInfo['filepath'], '.'), 1));
                $postfixArr = array("doc", "docx", "pdf", "mht", "txt", 'html');

                $logInfo['title'] = '解析附件';
                $logInfo['message'] = $taskInfo['id'] . '_' . $taskInfo['attachname'] . "_" . date("Y-m-d H:i:s");
                $this->writeLog($logInfo, false);

                if (in_array($attachPostfix, $postfixArr)) {
                    $success = $this->ParsingResume($taskInfo);

                    $sql = 'UPDATE ResumeOCRTask SET status=?,totalcount=1,analysisedcount=?,updatedate=now(),enddate=now() WHERE id=?';
                    $var = [$success ? '07' : '08', $success ? 1 : 0, $taskInfo['id']];
                    Db::update($sql, $var);
                } else if ($attachPostfix == "zip") {
                    $zipResult = $this->zipToTasks($taskInfo['filepath'], $taskInfo);

                    if ($zipResult['count'] == 0) {
                        $logInfo['title'] = '附件zip解压失败';
                        $logInfo['message'] = $taskInfo['id'] . '_' . $taskInfo['attachname'] . "_" . date("Y-m-d H:i:s");
                        $this->writeLog($logInfo);

                        $sql = 'UPDATE ResumeOCRTask SET status=?,totalcount=0,analysisedcount=0,updatedate=now(),enddate=now() WHERE id=?';
                        $var = ['08', $taskInfo['id']];
                        Db::update($sql, $var);
                        continue;
                    }

                    $sql = 'UPDATE ResumeOCRTask SET totalcount=?,analysisedcount=0,updatedate=now() WHERE id=?';
                    $var = [$zipResult['count'], $taskInfo['id']];
                    Db::update($sql, $var);

                    $success = false;
                    foreach ($zipResult['tasks'] as $subTaskInfo) {
                        if (preg_match('/.+\.xlsx?/u', $subTaskInfo['filepath'])) {
                            if ($this->parseExcel($subTaskInfo, true)) $success = true;
                        } elseif ($this->ParsingResume($subTaskInfo)) {
                            $success = true;
                            $sql = 'UPDATE ResumeOCRTask SET analysisedcount=analysisedcount+1,updatedate=now(),enddate=now() WHERE id=?';
                            $var = [$taskInfo['id']];
                            Db::update($sql, $var);
                        }
                    }

                    $sql = 'UPDATE ResumeOCRTask SET status=?,updatedate=now(),enddate=now() WHERE id=?';
                    $var = [$success ? '07' : '08', $taskInfo['id']];
                    Db::update($sql, $var);
                } else if ($attachPostfix == "xls" || $attachPostfix == "xlsx") {
                    $this->parseExcel($taskInfo);
                } else {

                    $logInfo['title'] = '暂不解析该格式';
                    $logInfo['message'] = $taskInfo['id'] . '_' . $taskInfo['attachname'] . "_" . date("Y-m-d H:i:s");
                    $this->writeLog($logInfo);

                    $sql = 'UPDATE ResumeOCRTask SET status=?,totalcount=0,analysisedcount=0,updatedate=now(),enddate=now() WHERE id=?';
                    $var = ['07', $taskInfo['id']];
                    Db::update($sql, $var);

                    //$this->ParsingResume($taskInfo);
                }
                //exit;

                continue;
            } catch (\Exception $e) {
                $info = [];
                if (!empty($taskInfo) && is_array($taskInfo) && !empty($taskInfo['id'])) $info['taskid'] = $taskInfo['id'];
                Log::error(e_dump($e), $info, 'cmd');
                sleep(10);
            }
        }
    }

    protected function zipToTasks($path, $taskInfo) {
        $result = ['count' => 0, 'tasks' => []];
        if (empty($path) || !is_string($path)) return $result;

        $zipFilePath = rtrim($this->fileServer, '/') . "/" . ltrim($path, '/');
        $zip = new \ZipArchive;
        if (@$zip->open($zipFilePath) !== true) return $result;

        $taskInfo['delfile'] = true;
        for ($i = 0; $i < $zip->numFiles; $i++) {
            try {
                $zipFile = $zip->statIndex($i);
                $fileInfo = $zip->statIndex($i, \ZipArchive::FL_ENC_RAW);
                if ($fileInfo['crc'] == 0 || $fileInfo['size'] == 0) continue;

                $fileName = $fileInfo['name'];

                if ($zipFile['name'] !== $fileName) {
                    $encode = mb_detect_encoding($fileName, array('ASCII', 'CP936', 'GB2312', 'GBK', 'BIG5', 'UTF-8'));
                    if ($encode != "UTF-8") {
                        if ($encode == 'CP936') {
                            $encode = 'GBK';
                        }
                        $fileName = iconv($encode, "UTF-8//IGNORE", $fileName);
                    }
                }

                $attachPostfix = strtolower(substr(strrchr($fileName, '.'), 1));

                $subTaskInfo = $taskInfo;
                $subTaskInfo['attachname'] = basename($fileName);
                $subTaskInfo['filepath'] = $path . "." . $i . "." . $attachPostfix;

                $filePathTmp = rtrim($this->fileServer, '/') . "/" . $subTaskInfo['filepath'];

                $logInfo['title'] = '解压文件';
                $logInfo['message'] = $taskInfo['id'] . '_' . $subTaskInfo['attachname'] . "_" . date("Y-m-d H:i:s");
                $this->writeLog($logInfo, false);

                if (!@copy('zip://' . $zipFilePath . '#' . $zipFile['name'], $filePathTmp)) {
                    $this->writeLog([
                        'title' => '解压文件失败',
                        'message' => $taskInfo['id'] . '_' . $subTaskInfo['attachname'] . "_" . date("Y-m-d H:i:s"),
                        'fileInfo' => $fileInfo
                    ]);
                    continue;
                }
                $result['tasks'][] = $subTaskInfo;
            } catch (\Exception $e) {
                $info = ['path' => $path, 'index' => $i];
                if (!empty($fileName)) $info['fileName'] = $fileName;
                Log::error(e_dump($e), $info, 'cmd');
            }
        }
        @$zip->close();
        $result['count'] = count($result['tasks']);
        return $result;
    }

    protected function parseExcel($taskInfo, $isSubTask = false) {
        $subTasks = $this->excelToTasks($taskInfo, $isSubTask);

        if (count($subTasks) == 0) {
            $this->writeLog([
                'title' => 'Excel文件读取失败',
                'message' => $taskInfo['id'] . '_' . $taskInfo['attachname'] . "_" . date("Y-m-d H:i:s")
            ]);

            if (!$isSubTask) {
                $sql = 'UPDATE ResumeOCRTask SET status=?,totalcount=0,analysisedcount=0,updatedate=now(),enddate=now() WHERE id=?';
                $var = ['08', $taskInfo['id']];
                Db::update($sql, $var);
            }
        }

        if ($isSubTask) {
            $sql = 'UPDATE ResumeOCRTask SET totalcount=totalcount+?,updatedate=now() WHERE id=?';
            $var = [count($subTasks) - 1, $taskInfo['id']];
            Db::update($sql, $var);
        } else {
            $sql = 'UPDATE ResumeOCRTask SET totalcount=?,analysisedcount=0,updatedate=now() WHERE id=?';
            $var = [count($subTasks), $taskInfo['id']];
            Db::update($sql, $var);
        }

        $success = false;
        foreach ($subTasks as $subTaskInfo) {
            if ($this->ParsingResume($subTaskInfo)) {
                $success = true;
                $sql = 'UPDATE ResumeOCRTask SET analysisedcount=analysisedcount+1,updatedate=now(),enddate=now() WHERE id=?';
                $var = [$taskInfo['id']];
                Db::update($sql, $var);
            }
        }
        if (!$isSubTask) {
            $sql = 'UPDATE ResumeOCRTask SET status=?,updatedate=now(),enddate=now() WHERE id=?';
            $var = [$success ? '07' : '08', $taskInfo['id']];
            Db::update($sql, $var);
        }
        return $success;
    }

    protected function preprocessExcel(PhpSpreadsheet\Spreadsheet $workbook, int $source, $autoTry = true) {
        if (isset($this->excelPreprocessors[$source])) {
            if ($this->excelPreprocessors[$source]->preprocess($workbook)) return true;
        }
        if ($autoTry) {
            foreach ($this->excelPreprocessors as $i => $preprocessor) {
                if ($i == $source) continue;
                if ($preprocessor->preprocess($workbook)) return true;
            }
        }
        return false;
    }

    protected function excelToTasks($taskInfo, $delFile = false) {
        $result = [];
        if (empty($taskInfo['filepath']) || !is_string($path = $taskInfo['filepath'])) return $result;

        $fullPath = rtrim($this->fileServer, '/') . "/" . ltrim($path, '/');
        try {
            $sourceWorkbook = PhpSpreadsheet\IOFactory::load($fullPath);
            $sheetCount = $sourceWorkbook->getSheetCount();
            if ($sheetCount < 1) return $result;
            if ($sheetCount == 1) return [$taskInfo];

            $taskInfo['delfile'] = true;
            $i = 0;
            $this->preprocessExcel($sourceWorkbook, $taskInfo['source']);
            foreach ($sourceWorkbook->getSheetNames() as $sheetName) {
                try {
                    //部分excel简历会导出索引页。暂时这样处理
                    if (preg_match('/下载*.*简历.*列表|索引|userlist/', strtolower($sheetName))) {
                        continue;
                    }
                    $resumeSheet = $sourceWorkbook->getSheetByName($sheetName);
                    if ($resumeSheet->getHighestRow() == 1 && $resumeSheet->getHighestColumn() == 'A') continue;
                    $resumeSheet->setSelectedCellByColumnAndRow(1, 1);

                    $singleResume = new PhpSpreadsheet\Spreadsheet();
                    $singleResume->removeSheetByIndex(0);
                    $singleResume->addExternalSheet($resumeSheet);
                    $writer = new PhpSpreadsheet\Writer\Xlsx($singleResume);

                    $subTaskInfo = $taskInfo;
                    //$subTaskInfo['attachname'] = preg_replace('/\.xlsx?$/u', '_' . $sheetName . '.xlsx', $taskInfo['attachname']);    //原文件名_工作表名
                    $subTaskInfo['attachname'] = preg_replace('/[\/\\\\\?%\*:\|"<>]/u', '_', $sheetName) . '.xlsx';
                    $subTaskInfo['filepath'] = $path . "." . $i . ".xlsx";
                    $writer->save(rtrim($this->fileServer, '/') . "/" . $subTaskInfo['filepath']);
                    $result[] = $subTaskInfo;
                    $i++;
                    $this->writeLog(['title' => '分离工作表', 'message' => $taskInfo['id'] . '_' . $subTaskInfo['attachname'] . "_" . date("Y-m-d H:i:s")], false);
                } catch (\Exception $e) {
                    Log::error(e_dump($e), ['path' => $path, 'sheetName' => $sheetName], 'cmd');
                }
            }
        } catch (\Exception $e) {
            Log::error(e_dump($e), ['path' => $path], 'cmd');
        } finally {
            if ($delFile) @unlink($fullPath);
        }
        return $result;
    }

    protected function getConfig($path, $ignoreError = false, $default = ['taskid' => 0]) {
        //配置文件的读取
        if (!file_exists($path)) {
            $logInfo['title'] = '配置文件不存在，自动初始化';
            $logInfo['message'] = $path;
            $logInfo['context'] = $default;
            $this->writeLog($logInfo);
            if (!$ignoreError) exit;
            file_put_contents($path, json_encode($default));
        }
        $config = @json_decode(file_get_contents($path), TRUE);
        if (empty($config)) {
            $logInfo['title'] = '配置文件格式异常';
            $logInfo['message'] = $path;
            $logInfo['context'] = file_get_contents($path);
            $this->writeLog($logInfo);
            if (!$ignoreError) exit;
            file_put_contents($path, json_encode($default));
            $config = $default;
        }
        return $this->readConfig($config, $default);
    }

    /*
     * 解析简历文件
     */
    public function ParsingResume($taskInfo) {
        $result = $this->getResumeInfo($taskInfo);
        if (!empty($result)) return $this->saveResume($taskInfo, $result);

        $this->saveResume($taskInfo, false);
        return false;
    }

    /**
     * 将简历数据源解析为标准的待入库简历数据结构
     * @param array $taskInfo 任务信息
     * @param bool $autoTry 为true时，若简历和指定渠道解析逻辑匹配失败，自动尝试其他渠道进行匹配
     * @return array|false 成功返回简历数据结构，否则返回false
     * @throws \Exception
     */
    public function getResumeInfo(array $taskInfo, $autoTry = true) {
        $filePathTmp = rtrim($this->fileServer, '/') . "/" . ltrim($taskInfo['filepath'], '/');
        if (!file_exists($filePathTmp)) {
            return false;
        }

        $resumeTxtArr = $this->readFileHelper->read($taskInfo, $autoTry);

        if (!$resumeTxtArr) {
            return false;
        }

        $resumeTxtArr['filepath'] = $taskInfo['filepath'];
        $resumeTxtArr['fileSource'] = $this->useMachineLearning ? 20 : $taskInfo['source'];
        $resumeTxtArr['taskid'] = $taskInfo['id'];
        $resumeTxtArr['urgent'] = $taskInfo['urgent'] ?? false;     //是否为前台调起的解析任务

        if ($this->enableMachineLearning) {
            $adapter = new ParsingService\DataStructAdapter($resumeTxtArr);
            if ($adapter->isValid) {
                $resumeTxtArr['resumeText'] = $adapter->getPlainText();
            }
//            if (!empty($resumeTxtArr['resumeDom'])
//                && property_exists($resumeTxtArr['resumeDom'], 'textContent')
//                && !empty($resumeTxtArr['resumeDom']->textContent)) {
//                $resumeTxtArr['resumeText'] = $resumeTxtArr['resumeDom']->textContent;
//            }
            //var_dump($adapter->getStructuredData());
        }
        //如果通过libreoffice方式转成docx的，删除临时文件
        if (isset($resumeTxtArr['tmpFile']) && $resumeTxtArr['tmpFile'] == true && file_exists(rtrim($this->fileServer, '/') . "/" . $resumeTxtArr['filepath'])) {
            @unlink(rtrim($this->fileServer, '/') . "/" . $resumeTxtArr['filepath']);
        }
        $resumeInfo = $this->parsingHelper->getResume($resumeTxtArr, $this->useMachineLearningOnly ? false : $autoTry);
        if (!$resumeInfo) {
            Log::error('简历解析内容获取失败', ['filePath' => $filePathTmp], 'cmd');
            return false;
        }

        return $this->translateHelper->translateResume($resumeInfo['resumeInfo']);
    }

    /*
     * 保存解析的结果到数据库
     */
    public function saveResume($taskInfo, $resumeInfo) {
        $filePathTmp = rtrim($this->fileServer, '/') . "/" . ltrim($taskInfo['filepath'], '/');

        $fileProfix = config('app.resume_machine_learning.log_file_path') . '/';
        if (!$resumeInfo) {
            $logInfo['title'] = '无法识别内容，不可入库';
            $logInfo['message'] = $taskInfo['id'] . '_' . $taskInfo['attachname'] . "_" . date("Y-m-d H:i:s");
            $this->writeLog($logInfo);

            if ($taskInfo['delfile']) {
                @unlink($filePathTmp);
            }
            if (!empty($taskInfo['urgent'])) throw new \Exception('无法识别内容，不可入库');

            $fileFailPath = $fileProfix . '/fail/' . floor($taskInfo['id'] / 2000);
            if (!file_exists($fileFailPath)) {
                File::makeDirectory($fileFailPath, 0777, true, true);
            }
            $filePathArr = explode('.', $taskInfo['filepath']);
            $fileType = strtolower(end($filePathArr));
            if (!file_exists($fileFailPath . '/' . $taskInfo['id'] . '.' . $fileType)) {
                if (array_slice($filePathArr, -3, 1)[0] == 'zip') {
                    $filePathTmp = preg_replace('/zip.*/', 'zip', $filePathTmp);
                    @copy($filePathTmp, $fileFailPath . '/' . $taskInfo['id'] . '.zip');
                } else {
                    @copy($filePathTmp, $fileFailPath . '/' . $taskInfo['id'] . '.' . $fileType);
                }

            }
            return false;
        }

        //以下逻辑机器识别不再使用
        if (1 == 0 && ($resumeInfo['mobile'] == '' || $resumeInfo['name'] == '')) {
            $logInfo['title'] = '没有识别到手机姓名信息，不可入库';
            $logInfo['message'] = $taskInfo['id'] . '_' . $taskInfo['attachname'] . "_" . date("Y-m-d H:i:s");
            $logInfo['mobile'] = $resumeInfo['mobile'];
            $logInfo['name'] = $resumeInfo['name'];
            $this->writeLog($logInfo);

            if ($taskInfo['delfile']) {
                @unlink($filePathTmp);
            }
            if (!empty($taskInfo['urgent'])) throw new \Exception('没有识别到手机姓名信息，不可入库');
            return false;
        }

        $rollbackIfNoAffect = true;

        //姓名手机为空时，处理成无法识别 文字内容
        if (empty($resumeInfo['name'])) $resumeInfo['name'] = '无法识别';
        if (empty($resumeInfo['mobile'])) $resumeInfo['mobile'] = '无法识别';
        $trimName = $this->formatName($resumeInfo['name']);

        $isGoodResume = true;   //根据姓名、手机判断是否需要进入去重功能
        if (empty($trimName) || empty($resumeInfo['mobile']) || $trimName == '无法识别' || $resumeInfo['mobile'] == '无法识别') {
            $isGoodResume = false;
        }

        //若启用不去重选项，则机器学习结果跳过去重逻辑
        if ($isGoodResume && $this->checkRepeat) {
            $sql = 'select resumeid,cname,status from RpoResume where phone=? and trimcname = ? and status=? order by resumeid desc';
            $var = [$resumeInfo['mobile'], $trimName, '01'];
            $seqInfo = Db::selectOne($sql, $var);

            //$resumeList['resumeid'] = 88;
            if (!empty($seqInfo) && count($seqInfo) > 0) {
                $mysqlStatements = [];
                $sql = 'INSERT INTO RpoResumeEditHis (oprid,oprtype,resumeid,createdate,updatedate)
                                          values (?,    ?,      ?,        Now(),    Now())';
                $var = [$taskInfo['creator'], '02', $seqInfo['resumeid']];//TODO已存在
                $mysqlStatements[] = Db::connect('jyrpo')->prepareStatement($sql, $var);

                //用户上传简历关系，新增方式
                $sql = "INSERT INTO UploadResume (resumeid,taskid,oprid,status,source,createdate,updatedate) 
                                      values (?,       ?,     ?,    ?,     ?,     Now(),     Now())";
                $var = [$seqInfo['resumeid'], $taskInfo['id'], $taskInfo['creator'], '01', $taskInfo['source']];
                $mysqlStatements[] = Db::connect('jyrpo')->prepareStatement($sql, $var);

                $result = Db::connect('jyrpo')->prepareTransactionStatement($rollbackIfNoAffect, ...$mysqlStatements)->execute();

                if (!$result) {
                    $logInfo['title'] = '附件保存至数据库失败';
                    $logInfo['message'] = $taskInfo['id'] . '_' . $taskInfo['attachname'] . "_" . $taskInfo['filepath'] . "_" . date("Y-m-d H:i:s");
                    $this->writeLog($logInfo);
                    return false;
                }

                if ($taskInfo['caseid'] > 0) {
                    //调用接口保存简历、case关系
                    $caseModel = new CaseModel();
                    $result = $caseModel->addToCase($taskInfo['creator'], $taskInfo['caseid'], [$seqInfo['resumeid']]);
                    if ($result['status'] != '0') {
                        $logInfo['title'] = '导入简历关联Case失败';
                        $logInfo['message'] = $result['message'];
                        $logInfo['context'] = 'creator:' . $taskInfo['creator'] . ';caseid:' . $taskInfo['caseid'] . ';resumeid:' . $seqInfo['resumeid'];
                        $this->writeLog($logInfo);
                    }
                }

                $logInfo['title'] = '数据库执行成功';
                $logInfo['message'] = $taskInfo['id'] . '_' . $taskInfo['attachname'] . "_" . $taskInfo['filepath'] . "_" . date("Y-m-d H:i:s");
                $this->writeLog($logInfo, false);

                $this->saveAttach($filePathTmp, $seqInfo['resumeid'], '03', $taskInfo);
                return true;
            }
        }

        //只有新建（非重复上传）的简历将头像数据写入文件
        if (!empty($resumeInfo['avatar']) && is_array($resumeInfo['avatar']) && !empty($resumeInfo['avatar']['data'])) {  //线上无法连接外网，屏蔽url下载方式
            try {
                $imgPath = $this->tmpDir . $taskInfo['id'] . $resumeInfo['avatar']['name'];
                file_put_contents($imgPath, $resumeInfo['avatar']['data'] ?? fopen($resumeInfo['avatar']['url'], 'rb'));
                $fileHandler = new FileHandler($imgPath);
                $moveResult = $fileHandler->move(true, $this->avatarDir);
                $resumeInfo['avatar'] = $moveResult['relativepathname'];
            } catch (\Exception$e) {
                $resumeInfo['avatar'] = '';
            }
        } else {
            $resumeInfo['avatar'] = '';
        }

        $sqlStr = "select seq_manager_userid.nextval as resumeid,seq_account_accountid.nextval as accountid,seq_manager_managerid.nextval as managerid from dual";
        $seqInfo = Db::connect('jyrpo_oracle')->selectOne($sqlStr);
        if (empty($seqInfo) || !isset($seqInfo['resumeid'])) {
            $logInfo['title'] = 'ORACLE数据库获取SEQ失败';
            $logInfo['message'] = $taskInfo['id'] . '_' . $taskInfo['attachname'] . "_" . $taskInfo['filepath'] . "_" . date("Y-m-d H:i:s");
            $this->writeLog($logInfo);
            return false;
        }

        $this->saveUndefinedCode($resumeInfo, $seqInfo['resumeid']);
        $savedResumeInfo = [];

        $sql = 'INSERT INTO RpoResumeEditHis (oprid,oprtype,resumeid,createdate,updatedate)
                                      values (?,    ?,      ?,        Now(),    Now())';
        $var = [$taskInfo['creator'], '01', $seqInfo['resumeid']];
        $tmpStatement = Db::connect('jyrpo')->prepareStatement($sql, $var);
        $transStatements[] = Db::connect('jyrpo')->prepareTransactionStatement($rollbackIfNoAffect, $tmpStatement);

        //用户上传简历关系，新增方式
        $sql = "INSERT INTO UploadResume (resumeid,taskid,oprid,status,source,  createdate,updatedate) 
                                  values (?,       ?,     ?,    ?,     ?,       Now(),     Now())";
        $var = [$seqInfo['resumeid'], $taskInfo['id'], $taskInfo['creator'], '01', $taskInfo['source']];
        $savedResumeInfo = array_merge($savedResumeInfo, [
            'resumeid' => $seqInfo['resumeid'],
            'taskid' => $taskInfo['id'],
            'source' => $taskInfo['source']
        ]);
        $tmpStatement = Db::connect('jyrpo')->prepareStatement($sql, $var);
        $transStatements[] = Db::connect('jyrpo')->prepareTransactionStatement($rollbackIfNoAffect, $tmpStatement);

        //工作经历表
        $hasWorkInfo = '0';
        if (isset($resumeInfo['workinfo']) && count($resumeInfo['workinfo']) > 0) {
            $hasWorkInfo = '1';
            $workInfo = [];
            foreach ($resumeInfo['workinfo'] as $item) {
                if ($item['industry_code'] === '' && !empty($item['industry'])) { //行业code不存在时进行日志记录保存
                    $dictPath = config('app.resume_machine_learning.dict') . '/' . date('Ym');
                    if (!file_exists($dictPath)) {
                        File::makeDirectory($dictPath, 0777, true, true);
                    }
                    $info = date('Y-m-d') . "\t" . $seqInfo['resumeid'] . "\t" . str_replace("\n",' ',$item['industry']) . "\n";
                    file_put_contents($dictPath . '/industry.txt', $info, FILE_APPEND);
                }
                $sql = 'INSERT INTO ResumeWork (resumeid,company,duty,salary,content,creator,createdate,updator,updatedate,status,workindustry,workindustrytext, starttime,              endtime         ) 
                                        values (?,       ?,      ?,   ?,     ?,      ?,       Now(),     ?,     Now(),     ?,     ?,           ?,           STR_TO_DATE(?,\'%Y-%m-%d\'),STR_TO_DATE(?,\'%Y-%m-%d\'))';
                $workInfoTmp = [
                    'company' => mb_substr($item['company'], 0, 200),
                    'duty' => mb_substr($item['duty'], 0, 100),
                    'salary' => mb_substr($item['salary'], 0, 100),
                    'content' => mb_substr($item['comment'], 0, 5000),
                    'creator' => $taskInfo['creator'],
                    'updator' => $taskInfo['creator'],
                    'workindustry' => $item['industry_code'],
                    'workindustrytext' => mb_substr($item['industry'], 0, 200),
                    'starttime' => $item['time_from'],
                    'endtime' => $item['time_end'] == '至今' ? '' : $item['time_end']

                ];
                $workInfo[] = $workInfoTmp;
                $var = [$seqInfo['resumeid'],
                    $workInfoTmp['company'],
                    $workInfoTmp['duty'],
                    $workInfoTmp['salary'],
                    $workInfoTmp['content'],
                    $taskInfo['creator'],
                    $taskInfo['creator'],
                    '01',
                    $item['industry_code'],
                    $workInfoTmp['workindustrytext'],
                    $item['time_from'],
                    $item['time_end']];
                $tmpStatement = Db::connect('jyrpo')->prepareStatement($sql, $var);
                $transStatements[] = Db::connect('jyrpo')->prepareTransactionStatement($rollbackIfNoAffect, $tmpStatement);
            }
            $savedResumeInfo = array_merge($savedResumeInfo, ['workinfo' => $workInfo]);
        }

        //项目经验表
        $hasProjectInfo = '0';
        if (isset($resumeInfo['projectinfo']) && count($resumeInfo['projectinfo']) > 0) {
            $hasProjectInfo = '1';
            $projectInfo = [];
            foreach ($resumeInfo['projectinfo'] as $item) {
                $sql = 'INSERT INTO ResumeProject (resumeid,      starttime,                  endtime,         company,duty,project,content,creator,createdate,updator,updatedate,status) 
                                           values (?, STR_TO_DATE(?,\'%Y-%m-%d\'),STR_TO_DATE(?,\'%Y-%m-%d\'),  ?,      ?,   ?,     ?,      ?,      Now(),     ?,      Now(),     ?)';
                $projectInfoTmp = [
                    'starttime' => $item['time_from'],
                    'endtime' => $item['time_end'] == '至今' ? '' : $item['time_end'],
                    'company' => mb_substr($item['company'], 0, 200),
                    'duty' => mb_substr($item['duty'], 0, 100),
                    'project' => mb_substr($item['project'], 0, 200),
                    'content' => mb_substr($item['comment'], 0, 5000)
                ];
                $projectInfo[] = $projectInfoTmp;
                $var = [$seqInfo['resumeid'],
                    $item['time_from'],
                    $item['time_end'],
                    $projectInfoTmp['company'],
                    $projectInfoTmp['duty'],
                    $projectInfoTmp['project'],
                    $projectInfoTmp['content'],
                    $taskInfo['creator'],
                    $taskInfo['creator'],
                    '01'];
                $tmpStatement = Db::connect('jyrpo')->prepareStatement($sql, $var);
                $transStatements[] = Db::connect('jyrpo')->prepareTransactionStatement($rollbackIfNoAffect, $tmpStatement);
            }
            $savedResumeInfo = array_merge($savedResumeInfo, ['projectinfo' => $projectInfo]);
        }
        //教育经历表
        $hasEducationInfo = '0';
        if (isset($resumeInfo['educationinfo']) && count($resumeInfo['educationinfo']) > 0) {
            $hasEducationInfo = '1';
            $educationInfo = [];
            foreach ($resumeInfo['educationinfo'] as $item) {
                if (empty($item['school']) || is_null($item['school'])) {
                    continue;
                }
                if ($item['degree_code'] === '' && !empty($item['degree'])) { //专业code不存在时进行日志记录保存
                    $dictPath = config('app.resume_machine_learning.dict') . '/' . date('Ym');
                    if (!file_exists($dictPath)) {
                        File::makeDirectory($dictPath, 0777, true, true);
                    }
                    $info = date('Y-m-d') . "\t" . $seqInfo['resumeid'] . "\t" . str_replace("\n",' ',$item['degree']) . "\n";
                    file_put_contents($dictPath . '/degree.txt', $info, FILE_APPEND);
                }
                if ($item['major_code'] === '' && !empty($item['major'])) { //专业code不存在时进行日志记录保存
                    $dictPath = config('app.resume_machine_learning.dict') . '/' . date('Ym');
                    if (!file_exists($dictPath)) {
                        File::makeDirectory($dictPath, 0777, true, true);
                    }
                    $info = date('Y-m-d') . "\t" . $seqInfo['resumeid'] . "\t" . str_replace("\n",' ',$item['major']) . "\n";
                    file_put_contents($dictPath . '/major.txt', $info, FILE_APPEND);
                }
                $sql = 'INSERT INTO ResumeEdu (resumeid,starttime,endtime,school,major,degree,major_code,degree_code,content,creator,createdate,updator,updatedate,status) values (?, STR_TO_DATE(?,\'%Y-%m-%d\'),STR_TO_DATE(?,\'%Y-%m-%d\'),?,?,?,?,?,?,?,Now(),?,Now(),?)';
                $educationInfoTmp = [
                    'starttime' => $item['time_from'],
                    'endtime' => $item['time_end'] == '至今' ? '' : $item['time_end'],
                    'school' => mb_substr($item['school'], 0, 100),
                    'major' => mb_substr($item['major'], 0, 50),
                    'degree' => mb_substr($item['degree'], 0, 200),
                    'major_code' => $item['major_code'],
                    'degree_code' => $item['degree_code'],
                    'content' => mb_substr($item['comment'], 0, 5000),
                ];
                $educationInfo[] = $educationInfoTmp;
                $var = [$seqInfo['resumeid'],
                    $item['time_from'],
                    $item['time_end'],
                    $educationInfoTmp['school'],
                    $educationInfoTmp['major'],
                    $educationInfoTmp['degree'],
                    $item['major_code'],
                    $item['degree_code'],
                    $educationInfoTmp['content'],
                    $taskInfo['creator'],
                    $taskInfo['creator'],
                    '01'];
                $tmpStatement = Db::connect('jyrpo')->prepareStatement($sql, $var);
                $transStatements[] = Db::connect('jyrpo')->prepareTransactionStatement($rollbackIfNoAffect, $tmpStatement);
            }
            $savedResumeInfo = array_merge($savedResumeInfo, ['educationinfo' => $educationInfo]);
        }

        //简历表
        $sql = 'INSERT INTO RpoResume (resumeid,    source,      innerid,       innerupdatetime,  gender,         nation,     idcard,         politic,
                                       phone,       email,       age,           birthday,         cname,          workyear,    hukou,         area,
                                       expectarea,  salaryfrom,  salaryto,      expectsalaryfrom, expectsalaryto, degree,      major,         position,
                                       workindustry,career,      married,       school,           nofocuse,       skillinfo,   awardinfo,     selfintro,
                                       otherinfo,   hasworkinfo, hasprojectinfo,haseducationinfo, creator,        createdate,  updator,       updatedate,
                                       status,      trimcname,   jobterm,       situation,        entrytime,      headpath) 
                               values (?,           ?,           ?,             ?,                ?,              ?,            ?,            ?,
                                       ?,           ?,           ?, STR_TO_DATE(?,\'%Y-%m-%d\'),  ?,              ?,            ?,            ?,
                                       ?,           ?,           ?,             ?,                ?,              ?,            ?,            ?,
                                       ?,           ?,           ?,             ?,                ?,              ?,            ?,            ?,
                                       ?,           ?,           ?,             ?,                ?,              Now(),        ?,            Now(), 
                                       ?,           ?,           ?,             ?,                ?,              ?)';

        $savedResumeInfo = array_merge($savedResumeInfo, [
            'innerid' => mb_substr($resumeInfo['id'], 0, 50),
            'innerupdatetime' => mb_substr($resumeInfo['time'], 0, 50),
            'gender' => $resumeInfo['sex_code'],
            'nation' => mb_substr($resumeInfo['nation'], 0, 20),
            'idcard' => mb_substr($resumeInfo['identitycard'], 0, 20),
            'politic' => $resumeInfo['politic_code'],
            'phone' => mb_substr($resumeInfo['mobile'], 0, 15),
            'email' => mb_substr($resumeInfo['email'], 0, 100),
            'age' => $resumeInfo['age'],
            'birthday' => $resumeInfo['birthday'],
            'cname' => mb_substr($resumeInfo['name'], 0, 50),
            'workyear' => $resumeInfo['workyear_code'],
            'hukou' => mb_substr($resumeInfo['area_code'], 0, 6),
            'area' => mb_substr($resumeInfo['workarea_code'], 0, 6),
            'expectarea' => mb_substr($resumeInfo['expectarea_code'], 0, 100),
            'salaryfrom' => $resumeInfo['salaryfrom'],
            'salaryto' => $resumeInfo['salaryend'],
            'expectsalaryfrom' => $resumeInfo['expectsalaryfrom'],
            'expectsalaryto' => $resumeInfo['expectsalaryend'],
            'degree' => $resumeInfo['degree_code'],
            'major' => $resumeInfo['major_code'],
            'position' => mb_substr($resumeInfo['position'], 0, 100),
            'workindustry' => $resumeInfo['industry_code'],
            'career' => mb_substr($resumeInfo['career'], 0, 100),
            'married' => $resumeInfo['married_code'],
            'school' => mb_substr($resumeInfo['school'], 0, 100),
            'nofocuse' => mb_substr($resumeInfo['nofocuse'], 0, 1000),
            'skillinfo' => $resumeInfo['skillinfo'],
            'awardinfo' => $resumeInfo['awardinfo'],
            'selfintro' => $resumeInfo['commentinfo'],
            'otherinfo' => $resumeInfo['otherinfo'],
            'hasworkinfo' => $hasWorkInfo,
            'hasprojectinfo' => $hasProjectInfo,
            'haseducationinfo' => $hasEducationInfo,
            'creator' => $taskInfo['creator'],
            'updator' => $taskInfo['creator'],
            'trimcname' => mb_substr($trimName, 0, 50),
            'jobterm' => $resumeInfo['jobterm_code'],
            'situation' => $resumeInfo['current_situation_code'],
            'entrytime' => $resumeInfo['entrytime_code'],
            'headpath' => $resumeInfo['avatar']
        ]);
        $var = [
            $seqInfo['resumeid'],
            $resumeInfo['source'],
            $savedResumeInfo['innerid'],//mb_substr($resumeInfo['id'], 0, 50),
            $savedResumeInfo['innerupdatetime'],
            $resumeInfo['sex_code'],
            $savedResumeInfo['nation'],
            $savedResumeInfo['idcard'],
            $resumeInfo['politic_code'],
            $savedResumeInfo['phone'],
            $savedResumeInfo['email'],
            $resumeInfo['age'],
            $resumeInfo['birthday'],
            $savedResumeInfo['cname'],
            $resumeInfo['workyear_code'],
            $savedResumeInfo['hukou'],
            $savedResumeInfo['area'],
            $savedResumeInfo['expectarea'],
            $resumeInfo['salaryfrom'],
            $resumeInfo['salaryend'],
            $resumeInfo['expectsalaryfrom'],
            $resumeInfo['expectsalaryend'],
            $resumeInfo['degree_code'],
            $resumeInfo['major_code'],
            $savedResumeInfo['position'],
            $resumeInfo['industry_code'],
            $savedResumeInfo['career'],
            $resumeInfo['married_code'],
            $savedResumeInfo['school'],
            $savedResumeInfo['nofocuse'],
            $resumeInfo['skillinfo'],
            $resumeInfo['awardinfo'],
            $resumeInfo['commentinfo'],
            $resumeInfo['otherinfo'],
            $hasWorkInfo,
            $hasProjectInfo,
            $hasEducationInfo,
            $taskInfo['creator'],
            $taskInfo['creator'],
            '01',
            $savedResumeInfo['trimcname'],
            $resumeInfo['jobterm_code'],
            $resumeInfo['current_situation_code'],
            $resumeInfo['entrytime_code'],
            $resumeInfo['avatar']
        ];
        $tmpStatement = Db::connect('jyrpo')->prepareStatement($sql, $var);
        $transStatements[] = Db::connect('jyrpo')->prepareTransactionStatement($rollbackIfNoAffect, $tmpStatement);

        //$mysqlTransStatements = Db::connect('jyrpo')->prepareTransactionStatement($rollbackIfNoAffect, ...$mysqlStatements);

        //s_account
        $sql = 'INSERT INTO s_account (accountid, mastermgrid, createdate, lastupdate, activedate, isactive, dbid, isjyacc) values (?,         ?,           sysdate,    sysdate,    sysdate,    ?,        ?,    ?)';
        $var = [$seqInfo['accountid'], $seqInfo['managerid'], 1, 1, 1];
        $rollbackIfNoAffect = true;
        $tmpStatement = Db::connect('jyrpo_oracle')->prepareStatement($sql, $var);
        $transStatements[] = Db::connect('jyrpo_oracle')->prepareTransactionStatement($rollbackIfNoAffect, $tmpStatement);

        //s_manager
        $sql = "INSERT INTO s_manager (ManagerID,    AccountID51job, UserID51job, ResumeDate,     CName,        Email,         MobilePhone,  Position, 
                                       WorkIndustry, Gender,         Area,        ExpectArea,     WorkYear,     Birthday,                    Company,
                                       School,       Major,          Degree,      CurrSalaryFrom, CurrSalaryTo, ExpSalaryFrom, ExpSalaryTo,
                                       Profile,      relationaccid,  agenttype,   activedate,     createdate,   lastupdate,    isactive,     isprofile,
                                       openstatus,   ownerspyid,     source,      isjycv) 
                                values (?,           ?,              ?,           sysdate,        ?,            ?,             ?,           ?,
                                        ?,           ?,              ?,           ?,              ?,            TO_DATE(?, 'YYYY-MM-DD'),   ?,
                                        ?,           ?,              ?,           ?,              ?,            ?,             ?,           
                                        ?,           ?,              '2',         sysdate,        sysdate,       sysdate,       '1',          '1',
                                        '2',         0,              8,           1)";

        $savedResumeInfo = array_merge($savedResumeInfo, [
//            'ManagerID' => $seqInfo['accountid'],
            'accountid51job' => $seqInfo['accountid'],
            'userid51job' => $seqInfo['resumeid'],
//            'CName' => mb_substr($resumeInfo['name'], 0, 9),
//            'Email' => mb_substr($resumeInfo['email'], 0, 49),
            'mobilephone' => mb_strcut($resumeInfo['mobile'], 0, 15),
//            'Position' => mb_substr($resumeInfo['position'], 0, 49),
//            'WorkIndustry' => $resumeInfo['industry_code'],
//            'Gender' => $resumeInfo['sex_code'],
//            'Area' => substr($resumeInfo['workarea_code'], 0, 6),
//            'ExpectArea' => substr($resumeInfo['expectarea_code'], 0, 100),
//            'WorkYear' => $resumeInfo['workyear_code'],
//            'Birthday' => $resumeInfo['birthday'],
            'company' => mb_substr($resumeInfo['company'], 0, 49),
//            'School' => mb_substr($resumeInfo['school'], 0, 49),
//            'Major' => $resumeInfo['major_code'],
//            'Degree' => $resumeInfo['degree_code'],
            'currsalaryfrom' => $resumeInfo['salaryfrom'],
            'currsalaryto' => $resumeInfo['salaryend'],
            'expsalaryfrom' => $resumeInfo['expectsalaryfrom'],
            'expsalaryto' => $resumeInfo['expectsalaryend']
        ]);
        $var = [$seqInfo['managerid'],
            $seqInfo['accountid'],
            $seqInfo['resumeid'],
            mb_substr($resumeInfo['name'], 0, 9),
            mb_substr($resumeInfo['email'], 0, 49),
            $savedResumeInfo['mobilephone'],//mb_strcut($resumeInfo['mobile'], 0, 15),
            mb_substr($resumeInfo['position'], 0, 49),
            $resumeInfo['industry_code'],
            $resumeInfo['sex_code'],
            substr($resumeInfo['workarea_code'], 0, 6),
            substr($resumeInfo['expectarea_code'], 0, 100),
            $resumeInfo['workyear_code'],
            $resumeInfo['birthday'],
            $savedResumeInfo['company'],
            mb_substr($resumeInfo['school'], 0, 49),
            $resumeInfo['major_code'],
            $resumeInfo['degree_code'],
            $resumeInfo['salaryfrom'],
            $resumeInfo['salaryend'],
            $resumeInfo['expectsalaryfrom'],
            $resumeInfo['expectsalaryend'],
            $resumeInfo['profile'],
            $seqInfo['accountid']
        ];

        if (config('app.resume_machine_learning.save_log_file')) {
            $filePath = $fileProfix . 'success/' . floor($seqInfo['resumeid'] / 2000);
            if (!file_exists($filePath)) {
                File::makeDirectory($filePath, 0777, true, true);
            }
            if (!empty($resumeInfo['jsonData'])) {
                file_put_contents($filePath . '/' . $seqInfo['resumeid'] . '_' . $taskInfo['id'] . '.json',
                    json_encode($resumeInfo['jsonData'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR));
            }
            if (!empty($resumeInfo['data'])) {
                file_put_contents($filePath . '/' . $seqInfo['resumeid'] . '_' . $taskInfo['id'] . '.txt', $resumeInfo['data']);
            }
            file_put_contents($filePath . '/' . $seqInfo['resumeid'] . '_' . $taskInfo['id'] . '.txt.json',
                json_encode($savedResumeInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR));
        }
        //var_dump($var);
        //var_dump('debug1');
        //$result = Db::connect('jyrpo_oracle')->prepareStatement($sql, $var);
        //var_dump('debug2', $result);exit;

        $tmpStatement = Db::connect('jyrpo_oracle')->prepareStatement($sql, $var);
        $transStatements[] = Db::connect('jyrpo_oracle')->prepareTransactionStatement($rollbackIfNoAffect, $tmpStatement);

        //$ = Db::connect('jyrpo_oracle')->prepareTransactionStatement($rollbackIfNoAffect, ...$oracleStatements);

        //使用跨库执行方法 multiTransaction 生成跨库句柄并调用execute方法
        $result = Db::multiTransaction(...$transStatements)->execute();
        if (!$result) {
            $logInfo['title'] = 'ORACLE保存数据库失败';
            $logInfo['message'] = $taskInfo['id'] . '_' . $taskInfo['attachname'] . "_" . $taskInfo['filepath'] . "_" . date("Y-m-d H:i:s");
            $this->writeLog($logInfo);
            return false;
        }

        if ($taskInfo['caseid'] > 0) {
            //调用接口保存简历、case关系
            $caseModel = new CaseModel();
            $result = $caseModel->addToCase($taskInfo['creator'], $taskInfo['caseid'], [$seqInfo['resumeid']]);
            if ($result['status'] != '0') {
                $logInfo['title'] = '导入简历关联Case失败';
                $logInfo['message'] = $result['message'];
                $logInfo['context'] = 'creator:' . $taskInfo['creator'] . ';caseid:' . $taskInfo['caseid'] . ';resumeid:' . $seqInfo['resumeid'];
                $this->writeLog($logInfo);
            }
        }

        $logInfo['title'] = '数据库执行成功';
        $logInfo['message'] = $taskInfo['id'] . '_' . $taskInfo['attachname'] . "_" . $taskInfo['filepath'] . "_" . date("Y-m-d H:i:s");
        $this->writeLog($logInfo, false);
        $this->saveAttach($filePathTmp, $seqInfo['resumeid'], '01', $taskInfo);
        //RpoResume表新增记录，需要将resumeid同步到es引擎
        $resumeUpdate = new ResumeUpdate();
        $resumeUpdate->addToQueue($seqInfo['resumeid']);
        return true;
    }

    /*
     * 保存简历附件
     */
    function saveAttach($filePathTmp, $resumeid, $type, &$taskInfo) {
        $resumeModel = new ResumeModel();
        $fileObj = new FileHelper($filePathTmp);
        $result = $resumeModel->addAttach($fileObj, $taskInfo['creator'], $resumeid, '01', $taskInfo['attachname'], !$taskInfo['delfile']);
        if (empty($result) || !isset($result['status']) || !$result['status']) {
            $logInfo['title'] = '附件保存失败';
            $logInfo['message'] = $taskInfo['id'] . '_' . $taskInfo['attachname'] . "_" . $taskInfo['filepath'] . "_" . date("Y-m-d H:i:s");
            $this->writeLog($logInfo);
            return;
        }

        $logInfo['title'] = '附件保存成功';
        $logInfo['message'] = $taskInfo['id'] . '_' . $taskInfo['attachname'] . "_" . $result['fileinfo']['relativepathname'] . "_" . date("Y-m-d H:i:s");
        $this->writeLog($logInfo, false);

        $taskInfo['attachid'] = $result['fileinfo']['fileid'];
        $taskInfo['fullpath'] = $result['fileinfo']['path'];
        $taskInfo['filepath'] = $result['fileinfo']['relativepathname'];
        //$attachSize = ceil($result['fileinfo']['filesize'] / 1024);


        $sql = 'UPDATE UploadResume SET attachid=?,type=? where taskid=? and resumeid=?';
        $var = [$taskInfo['attachid'], $type, $taskInfo['id'], $resumeid];
        Db::update($sql, $var);
    }

    /*
     * 只保留中英文数字。
     */
    function formatName($str) {
        $pattern = '/[\x{4e00}-\x{9fa5}a-zA-Z0-9]/u';
        preg_match_all($pattern, $str, $result);
        $str = join('', $result[0]);
        return strtolower($str);
    }

    /*
    * 读配置
    */
    function readConfig(&$source, &$template) {
        if (!is_array($source)) return $template;
        foreach ($template as $k => $v) {
            if (is_array($v)) {
                $this->readConfig($source[$k], $template[$k]);
            } elseif (isset($source[$k])) {
                //仅当配置文件内数值比默认值大，采用配置文件内容，否则使用默认值。
                if ($source[$k] >= $template[$k]) {
                    $template[$k] = $source[$k];
                }
            }
        }
        return $template;
    }

    /*
     * 写日志
     */
    function writeLog($logInfo, $toLogFile = true) {
        /*
        $context['title'] = '配置文件不存在，自动初始化';
        $context['message'] = $positionConfig;
        $context['context'] = $positionArr;
        */
        $title = $logInfo['title'];
        unset ($logInfo['title']);
        if (!empty($this->output)) {
            $this->output->writeln(PHP_EOL . "*********************************************************************************************************");
            $this->output->writeln(var_export($logInfo, true) . PHP_EOL);
            //$this->output->writeln("系统时间：" . date("Y-m-d H:i:s") . PHP_EOL);
            $this->output->writeln($this->description . '->' . $title . " 系统时间：" . date("Y-m-d H:i:s"));
            $this->output->writeln("*********************************************************************************************************" . PHP_EOL);
        }
        if ($toLogFile) Log::info($title, $logInfo, 'cmd');
    }

    /**
     * 当无法信息取得对应code的时候，将对应信息保存到文件中方便今后添加处理
     * @param $resumeInfo
     * @param $resumeId
     * @throws \Exception
     */
    function saveUndefinedCode(&$resumeInfo, $resumeId) {
        //性别code不存在时进行日志记录保存
        $dictPath = config('app.resume_machine_learning.dict') . '/' . date('Ym');
        if (!file_exists($dictPath)) {
            File::makeDirectory($dictPath, 0777, true, true);
        }
        $baseInfoArr = ['name', 'mobile', 'email'];
        $tmpInfo = date('Y-m-d') . "\t" . $resumeId . "\t";
        $flag = false;
        foreach ($baseInfoArr as $item) {
            if ($resumeInfo[$item] == '无法识别' || $resumeInfo[$item] == '') {
                $tmpInfo = $tmpInfo . $item . '无法识别' . "\t";
                $flag = true;
            }
        }
        if ($flag == true) {
            $tmpInfo .= "\n";
            file_put_contents($dictPath . '/resume.txt', $tmpInfo, FILE_APPEND);
        }

        $dictArr = ['sex', 'married', 'degree', 'industry', 'workyear', 'workarea', 'area',
            'expectarea', 'politic', 'major', 'jobterm', 'current_situation', 'entrytime'];
        foreach ($dictArr as $dict) {
            //当字典翻译失败，或者为解析有问题后台进行自动完善的情况下，记录到日志
            if (($resumeInfo[$dict . '_code'] === '' && !empty($resumeInfo[$dict])) || strstr($resumeInfo[$dict. '_code'], '_autofix')) {
                $info = date('Y-m-d') . "\t" . $resumeId . "\t" . str_replace("\n", ' ', $resumeInfo[$dict]) . "\n";
                file_put_contents($dictPath . '/' . $dict . '.txt', $info, FILE_APPEND);
            }
        }
        //后台修复的情况下带有_autofix后缀，这里进行去除
        foreach ($dictArr as $dict) {
            if (strstr($resumeInfo[$dict. '_code'], '_autofix')) {
                $resumeInfo[$dict. '_code'] = str_replace('_autofix','',$resumeInfo[$dict. '_code']);
            }
        }
        return;
    }

    protected function checkMachineLearning() {
        //识别前进行机器学习服务器测试
        if (config('app.resume_machine_learning.mode') && file_exists(__DIR__ . '/machinelearning_status.txt')) {
            $machinelearningStatus = file_get_contents(__DIR__ . '/machinelearning_status.txt');
            //如果上一次服务器未返回结果，则进行一次尝试
            if ($machinelearningStatus == 'error') {
                $testContent = '智联招聘
                                期望从事职业：
                                数据运营、销售数据分析
                                简历更新时间：
                                2019.04.18
                                ID：o(5PpaJQI4LMNyTdJRWmHA
                                邓昊麟
                                手机：18826243171
                                男 24岁 (1995年9月) 2年工作经验 本科
                                现居住地：佛山 佛山 | 户口：佛山 | 群众
                                手机：18826243171';
                $ch = curl_init(config('app.resume_machine_learning.url'));
                # Setup request to send json via POST.
                curl_setopt($ch, CURLOPT_TIMEOUT, 60);   //单位 秒，也可以使用
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array("upload_text" => array($testContent))));
                curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
                # Return response instead of printing.
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                $result = curl_exec($ch);
                curl_close($ch);
                $tmp = json_decode($result, true);
                //机器解析服务器存在错误，识别程序不继续往下执行
                if (!$tmp) {
                    return false;
                }
                //如果当前服务器能返回结果，则继续执行
                file_put_contents(__DIR__ . '/machinelearning_status.txt', 'correct');
            }
        }
        return true;
    }
}